﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows;
using System.Windows.Input;
using System.Windows.Media.Imaging;
using System.Collections.ObjectModel;
using CaptchaLearning.Infrastructure;
using CaptchaLearning.Infrastructure.Algorithm;
using CaptchaLearning.Infrastructure.Model;

namespace CaptchaLearning.Recognize
{
    public class RecognizeViewModel : WorkspaceViewModel
    {
        #region Fields and Properties

        private string _character;
        public string Character
        {
            get { return _character; }
            set 
            {
                _character = value;
                OnPropertyChanged("Character");
            }
        }

        private BitmapImage _image;
        public BitmapImage Image
        {
            get { return _image; }
            set { _image = value; OnPropertyChanged("Image"); }
        }

        private Character _currentCharacter;
        private System.Drawing.Bitmap _currentBitmap;

        private string _lblMatrix;
        public string LblMatrix
        {
            get { return _lblMatrix; }
            set
            {
                _lblMatrix = value;
                OnPropertyChanged("LblMatrix");
            }
        }

        private string _matrix;
        public string Matrix
        {
            get { return _matrix; }
            set
            {
                _matrix = value;
                OnPropertyChanged("Matrix");
            }
        }

        private bool _transitionMatrixChecked;
        public bool TransitionMatrixChecked
        {
            get { return _transitionMatrixChecked; }
            set
            {
                _transitionMatrixChecked = value;
                if (_transitionMatrixChecked)
                {
                    LoadTransitionMatrix(_currentCharacter);
                    LblMatrix = "Transition Matrix";
                }
                OnPropertyChanged("TransitionMatrixChecked");
            }
        }

        private bool _emissionMatrixChecked;
        public bool EmissionMatrixChecked
        {
            get { return _emissionMatrixChecked; }
            set
            {
                _emissionMatrixChecked = value;
                if (_emissionMatrixChecked)
                {
                    LoadEmissionMatrix(_currentCharacter);
                    LblMatrix = "Emission Matrix";
                }
                OnPropertyChanged("EmissionMatrixChecked");
            }
        }

        private bool _totalMatrixChecked;
        public bool TotalMatrixChecked
        {
            get { return _totalMatrixChecked; }
            set
            {
                _totalMatrixChecked = value;
                if (_totalMatrixChecked)
                {
                    LoadTotalMatrix(_currentCharacter);
                    LblMatrix = "Total Matrix";
                }
                OnPropertyChanged("TotalMatrixChecked");
            }
        }

        #endregion // Fields and Properties

        public RecognizeViewModel()
        {
            DisplayName = "Recognition";

            this._paintCommand = new DelegateCommand(new Action(ExecutePaintCommand), () => true);
            this._predictCommand = new DelegateCommand(new Action(ExecutePredictCommand), () => true);
        }

        #region Commands

        /// <summary>
        /// Thực hiện chạy giải thuật tìm ra mô hình HMM phù hợp nhất
        /// </summary>
        private ICommand _predictCommand;
        public ICommand PredictCommand
        {
            get { return _predictCommand; }
        }
        public void ExecutePredictCommand()
        {
            if (Image == null)
            {
                MessageBox.Show("You haven't draw anything yet");
                return;
            }
            ObservableCollection<Infrastructure.Model.ImageColumn> thinImage = GetThinImage(Image);
            Infrastructure.Model.Character character = GetBestCharacter(thinImage);
            _currentCharacter = character;
            Character = character.Name;
        }

        /// <summary>
        /// Thực hiện thao tác vẽ 1 ký tự, kết quả save file bmp xuống thư mục Image
        /// </summary>
        private ICommand _paintCommand;
        public ICommand PaintCommand
        { get { return _paintCommand; } }
        public void ExecutePaintCommand()
        {
            Painter.Painter paint = new Painter.Painter();
            paint.Name = "temp";
            if (Image != null)
                paint.Image = Image;
            paint.ShowDialog();
            if (paint.Save)
            {
                BitmapImage tmp = new BitmapImage();
                tmp.BeginInit();
                tmp.CreateOptions = BitmapCreateOptions.None;
                tmp.CacheOption = BitmapCacheOption.OnLoad;
                tmp.StreamSource = new FileStream(Path.Combine(Directory.GetCurrentDirectory(), "Image", "temp.bmp"),
                                            FileMode.Open, FileAccess.Read);
                tmp.EndInit();
                Image = tmp;
            }
        }

        /// <summary>
        /// Xóa hình của ký tự đó trong thư mục image
        /// </summary>
        private ICommand _clearCommand;
        public ICommand ClearCommand
        { get { return _clearCommand; } }
        public void ExecuteClearCommand()
        {
            if (string.IsNullOrEmpty(Character))
            {
                MessageBox.Show("You must select character first");
                return;
            }
            if (Image == null)
            {
                MessageBox.Show("Have no image to remove");
                return;
            }
            if (File.Exists(Path.Combine(Directory.GetCurrentDirectory(), "Image", "temp.bmp")))
                File.Delete(Path.Combine(Directory.GetCurrentDirectory(), "Image", "temp.bmp"));
            Image = null;
        }

        #endregion // Commands

        #region Helpers

        /// <summary>
        /// Scale tấm hình bitmap lại theo kích thước pixel rồi lấy danh sách thin image của nó
        /// </summary>
        private ObservableCollection<Infrastructure.Model.ImageColumn> GetThinImage(BitmapImage image)
        {
            ObservableCollection<Infrastructure.Model.ImageColumn> thinImages = new ObservableCollection<Infrastructure.Model.ImageColumn>();
            System.Drawing.Bitmap bitmap = ResizeImage(image, Global.CurrentProject.Pixels, Global.CurrentProject.Pixels);
            this._currentBitmap = bitmap;
            // TODO: get all column pixel in bitmap and add it into thinImages
            // ARGB value: Black->#FF000000, White->#FFFFFFFF
            // width: x, height: y

            Infrastructure.Model.ImageColumn tmpImageColumn;
            int _width = bitmap.Width, _height = bitmap.Height;
            System.Drawing.Color _color;
            for (int _w = 0; _w < _width; _w++)
            {
                tmpImageColumn = new Infrastructure.Model.ImageColumn();
                tmpImageColumn.Order = _w;

                for (int _h = 0; _h < _height; _h++)
                {
                    _color = bitmap.GetPixel(_w, _h);
                    if ((_color.R >= 254) && (_color.G >= 254) && (_color.B >= 254)) { tmpImageColumn.Pixels.Add(0); }
                    else { tmpImageColumn.Pixels.Add(1); }
                }
                thinImages.Add(tmpImageColumn);
            }
            return thinImages;
        }

        /// <summary>
        /// Scale image to width x height size
        /// </summary>
        public System.Drawing.Bitmap ResizeImage(BitmapImage image, int width, int height)
        {
            System.Drawing.Image source = System.Drawing.Image.FromStream(Image.StreamSource);

            System.Drawing.Bitmap result = new System.Drawing.Bitmap(width, height);

            result.SetResolution(source.HorizontalResolution, source.VerticalResolution);

            using (System.Drawing.Graphics graphics = System.Drawing.Graphics.FromImage(result))
            {
                graphics.CompositingQuality = System.Drawing.Drawing2D.CompositingQuality.HighQuality;
                graphics.InterpolationMode = System.Drawing.Drawing2D.InterpolationMode.HighQualityBicubic;
                graphics.SmoothingMode = System.Drawing.Drawing2D.SmoothingMode.HighQuality;
                graphics.DrawImage(source, 0, 0, result.Width, result.Height);
            }

            return result;
        }

        /// <summary>
        /// Duyệt danh sách character trong project, tính toán likelihood của chuỗi quan sát bằng mô hình HMM của ký tự đó
        /// Trả về ký tự có xác suất cao nhất
        /// </summary>
        private Infrastructure.Model.Character GetBestCharacter(ObservableCollection<Infrastructure.Model.ImageColumn> observations)
        {
            if (Global.CurrentProject.Characters.Count <= 0)
                return null;
            Evaluate evaluate = new Evaluate();
            int max = 0;
            double current = evaluate.ComputeLikelihood(Global.CurrentProject.Characters[0], observations);
            for (int i = 1; i < Global.CurrentProject.Characters.Count; i++)
            {
                double tmp = evaluate.ComputeLikelihood(Global.CurrentProject.Characters[i], observations);
                if (tmp > current)
                {
                    current = tmp;
                    max = i;
                }
            }
            return Global.CurrentProject.Characters[max];
        }

        /// <summary>
        /// Load ma trận transition của character lên
        /// </summary>
        private void LoadTransitionMatrix(Infrastructure.Model.Character character)
        {
            if (character == null)
                return;
            //For Test Transition
            string text = string.Empty;
            for (int i = 0; i < character.TransitionMatrix.StateCount; i++)
            {
                text += string.Format("{0,-5} || ", i);
                for (int j = 0; j < character.TransitionMatrix.StateCount; j++)
                {
                    text += string.Format("{0,-5:0.000} | ", character.TransitionMatrix.Probability[i][j]);
                }
                text += "|\n";
            }
            Matrix = text;


        }

        /// <summary>
        /// Load ma trận emission của character lên
        /// </summary>
        private void LoadEmissionMatrix(Infrastructure.Model.Character character)
        {
            if (character == null)
                return;
            string text = string.Empty;
            for (int i = 0; i < character.EmissionMatrix.StateCount; i++)
            {
                text += string.Format("{0,-5} || ", i);
                for (int j = 0; j < character.EmissionMatrix.ObservationCount; j++)
                {
                    text += string.Format("{0,-5:0.000} | ", character.EmissionMatrix.Probability[i][j]);
                }
                text += "|\n";
            }
            Matrix = text;
        }

        private void LoadTotalMatrix(Infrastructure.Model.Character character)
        {
            if (character == null)
                return;
            checkBitmap(_currentBitmap);
            checkObser(character);
        }

        private void checkBitmap(System.Drawing.Bitmap bitmap)
        {
            int _width = bitmap.Width, _height = bitmap.Height;
            System.Drawing.Color _color;
            string test = "Bitmap:\n";
            bitmap.Save("TestBitmap.png");
            for (int _w = 0; _w < _width; _w++)
            {
                test += "C" + _w;
                for (int _h = 0; _h < _height; _h++)
                {
                    _color = bitmap.GetPixel(_w, _h);
                    test += "[" + _color.A + "," + _color.R + "," + _color.G + "," + _color.B + "] ";
                }
                test += "\n";
            }
            test += "\n";
            Matrix = test;
        }

        private void checkObser(Infrastructure.Model.Character character)
        {
            string text = "Cot Pixel:\n";
            for (int i = 0; i < character.Observations[0].Pixels.Count; i++)
            {
                for (int j = 0; j < character.Observations.Count; j++)
                {
                    text += "" + character.Observations[j].Pixels[i];
                }
                text += "\n";
            }
            text += "\n";
            Matrix += text;
        }

        #endregion // Helpers
    }
}
